#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define LEFT 3000000
#define RIGHT 3000200
#define N 5

struct mysem_st
{
    int value;
    pthread_mutex_t mtx;
    pthread_cond_t cond;
};

typedef struct mysem_st mysem;

static mysem *mysem_init(int count)
{
    mysem *s = malloc(sizeof(mysem));

    if (s == NULL)
        return s;

    s->value = count;
    pthread_mutex_init(&s->mtx, NULL);
    pthread_cond_init(&s->cond, NULL);

    return s;
}

static mysem *mysem_add(mysem *m, int count)
{
    pthread_mutex_lock(&m->mtx);
    m->value += count;
    pthread_cond_broadcast(&m->cond);
    pthread_mutex_unlock(&m->mtx);
}

static int mysem_sub(mysem *m, int count)
{
    pthread_mutex_lock(&m->mtx);

    while (m->value < count)
    {
        pthread_cond_wait(&m->cond, &m->mtx);
    }

    m->value -= count;
    pthread_mutex_unlock(&m->mtx);
    return count;
}

static int mysem_destroy(mysem *m)
{
    pthread_mutex_destroy(&m->mtx);
    pthread_cond_destroy(&m->cond);
    free(m);
    return 0;
}

static struct check_arg {
    mysem* sem;
    int value;
};

static void *check_prime(void *p)
{
    struct check_arg* ptr = p;

    int i = ptr->value;
    int is_prime = 1;
    for (int j = 2; j < i / 2; j++)
    {
        if (i % j == 0)
        {
            is_prime = 0;
            break;
        }
    }

    if (is_prime)
        printf("%d is a prime number\n", i);

    mysem_add(ptr->sem, 1);
    return NULL;
}



int main()
{
    mysem *m = mysem_init(N);
    pthread_t threads[RIGHT - LEFT + 1];
    for (int i = LEFT; i <= RIGHT; i++)
    {
        mysem_sub(m, 1);
        struct check_arg a;
        a.value = i;
        a.sem = m;
        pthread_t pid;
        pthread_create(&pid, NULL, check_prime, &a);
        threads[i - LEFT] = pid;
    }


    for(int i = 0; i < RIGHT - LEFT + 1; i++) {
        pthread_join(threads[i], NULL);
    }
    mysem_destroy(m);
}